/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the lkyh.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.lkyh.mes.codegen.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lkyh.mes.codegen.dto.FormInfo;
import com.lkyh.mes.codegen.dto.QueryFormInfo;
import com.lkyh.mes.codegen.entity.*;
import com.lkyh.mes.codegen.mapper.GenFormActionMapper;
import com.lkyh.mes.codegen.service.*;
import com.lkyh.mes.codegen.util.FormUtils;
import ognl.DefaultMemberAccess;
import ognl.OgnlContext;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.regex.Pattern.compile;

/**
 * 表单管理
 *
 * @author lengleng
 * @date 2019-08-12 15:55:35
 */
@Service
//@AllArgsConstructor
public class GenFormActionServiceImpl extends ServiceImpl<GenFormActionMapper, GenFormAction> implements GenFormActionService {
	@Resource
	private GenFormDoService genFormDoService;

	@Resource
	private FormInfoService formInfoService;
	@Resource
	private GenTableInfoService genTableInfoService;

	@Resource
	private GenDataViewService genDataViewService;
	@Resource
	private GenDatasourceConfService genDatasourceConfService;
	@Resource
	private StringEncryptor stringEncryptor;
	@Resource
	private PlatformTransactionManager platformTransactionManager;


	@Override
	public List<GenFormAction> list(Wrapper<GenFormAction> queryWrapper) {
		List<GenFormAction> list = super.list(queryWrapper);
		OgnlContext context = new OgnlContext(null, null, new DefaultMemberAccess(true));
		list.forEach(i -> {
			context.put("data", i);
			i.setShow(verifyCondition(i.getShowCondition(), context));
		});
		return list;
	}

	@Override
	public boolean remove(Wrapper<GenFormAction> wrapper) {
		if (super.remove(wrapper)) {
			return genFormDoService.remove(Wrappers.query(new GenFormDo().setActionId(wrapper.getEntity().getId())));
		}
		return false;
	}

	@Override
//	@Transactional(rollbackFor = Exception.class)
	public boolean run(Integer id, Integer dataId) {
		GenFormAction genFormAction = this.getById(id);
		List<GenFormDo> genFormDoList = genFormDoService.listWithChild(new GenFormDo().setActionId(id));
		if (CollUtil.isNotEmpty(genFormDoList)) {
			OgnlContext context = new OgnlContext(null, null, new DefaultMemberAccess(true));

			Map<String, Object> one = formInfoService.getOne(new QueryFormInfo().setTableId(genFormAction.getTableId()).setCondition(new HashMap<String, String>() {{
				put("dataId", String.valueOf(dataId));
			}}));
			context.put("data", one);
			context.setRoot(one);
			//为了事务隔离,先全部查出来
			Map<Integer, GenTableInfo> collect = genFormDoList.stream().map(i -> genTableInfoService.getByIdWithChild(i.getDoTableId())).collect(Collectors.toMap(GenTableInfo::getId, i -> i));
//			//2.获取事务定义
//
//			DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//
//			//3.设置事务隔离级别，开启新事务
//
//			def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//
//			//4.获得事务状态
//
//			TransactionStatus status = platformTransactionManager.getTransaction(def);
			try {
				genFormDoList.sort(Comparator.comparing(GenFormDo::getOrder));
				for (GenFormDo genFormDo : genFormDoList) {
					GenTableInfo genTableInfo = collect.get(genFormDo.getDoTableId());
					String condition = genFormDo.getCondition();
					if (verifyCondition(condition, context)) {
						if (genFormDo.getType() == 1) {
							if ("insert".equalsIgnoreCase(genFormDo.getMethod())) {
								saveAndPut(genTableInfo, genFormDo, context);
							} else if ("batchInsert".equalsIgnoreCase(genFormDo.getMethod())) {
								Object iterObj = getVal(genFormDo.getIter(), context);
								if (iterObj instanceof Iterable) {
									Iterable<?> iter = (Iterable<?>) iterObj;
									iter.forEach(i -> {
										context.put("$item", i);
										if (verifyCondition(genFormDo.getIterCondition(), context)) {
											saveAndPut(genTableInfo, genFormDo, context);
										}
									});
								} else if (iterObj instanceof Number) {
									Number iter = (Number) iterObj;
									for (int i = 0; i < iter.intValue(); i++) {
										context.put("$item", i);
										saveAndPut(genTableInfo, genFormDo, context);
									}
								}
							} else if ("update".equalsIgnoreCase(genFormDo.getMethod())) {
								FormInfo formInfo = buildFormInfo(genTableInfo, genFormDo, context);
								List<Map<String, Object>> updateDataList = getDoDataList(context, genFormDo);
								if (CollUtil.isNotEmpty(updateDataList)) {
									updateDataList.forEach(i -> {
										formInfoService.update(formInfo.setId(Convert.toInt(i.get("id"))));
										Map<String, Object> newOne = formInfoService.getOne(new QueryFormInfo().setTableId(genFormDo.getDoTableId()).setCondition(new HashMap<String, String>() {{
											put("id", String.valueOf(i.get("id")));
										}}));
										Object o = context.get(genFormDo.getName());
										if (null != o) {
											List<Map<String, Object>> l = new ArrayList<>();
											if (o instanceof List) {
												l = (List<Map<String, Object>>) o;
											}
											l.add(newOne);
											context.put(genFormDo.getName(), l);
										} else {
											context.put(genFormDo.getName(), newOne);
										}
									});
								}

							} else if ("delete".equalsIgnoreCase(genFormDo.getMethod())) {
								List<Map<String, Object>> removeList = getDoDataList(context, genFormDo);
								if (CollUtil.isNotEmpty(removeList)) {
									formInfoService.batchRemove(genFormDo.getDoTableId(), StrUtil.join(StrUtil.COMMA, removeList.stream().map(i -> i.get("id")).collect(Collectors.toList())));
								}
							}
						} else {
							runDataView(genFormDo.getVal(), context);
						}
					}
				}
//				platformTransactionManager.commit(status);
			} catch (Exception e) {
//				platformTransactionManager.rollback(status);
				log.error("run error", e);
				throw e;
			}
		}
		return true;
	}

	private List<Map<String, Object>> getDoDataList(OgnlContext context, GenFormDo genFormDo) {
		Map<String, String> condition = new HashMap<>();
		condition.put(genFormDo.getKeyId(), Objects.requireNonNull(FormUtils.getVal(genFormDo.getKeyIdVal(), context)).toString());
		return formInfoService.list(new QueryFormInfo().setTableId(genFormDo.getDoTableId()).setCondition(condition));
	}

	private boolean verifyCondition(String condition, OgnlContext context) {
		if (StrUtil.isNotBlank(condition)) {
			return Boolean.parseBoolean(getVal(condition, context).toString());
		} else {
			return true;
		}
	}

	private GenDataView buildDdsAndArgs(String group, DriverManagerDataSource dds) {
		String dataView = StrUtil.subBetween(group, "@@", "(");
		GenDataView genDataView = genDataViewService.getOne(Wrappers.query(new GenDataView().setName(dataView)));
		GenDatasourceConf datasourceConf = genDatasourceConfService.getById(genDataView.getDatasourceId());
		dds.setUrl(datasourceConf.getUrl());
		dds.setDriverClassName(datasourceConf.getDriverClassName());
		dds.setUsername(datasourceConf.getUsername());
		dds.setPassword(stringEncryptor.decrypt(datasourceConf.getPassword()));

		genDataView.setParams(StrUtil.subBetween(group, "(", ")"));
		return genDataView;
	}

	private void runDataView(String expression, OgnlContext context) {
		if (expression.contains("@@")) {
			DriverManagerDataSource dds = new DriverManagerDataSource();
			List<String> allGroups = ReUtil.findAll(compile("@@(.*?)\\((.*?)\\)"), expression, 0);
			for (String group : allGroups) {
				GenDataView genDataView = buildDdsAndArgs(group, dds);
				if ("execute".equals(genDataView.getType())) {
					new JdbcTemplate(dds).update(genDataView.getSql(),
							Stream.of(genDataView.getParams().split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).map(
									i -> FormUtils.getVal(i, context)).toArray());
				}
			}
		}
	}

	private Object getVal(String expression, OgnlContext context) {
		String s = expression;
		if (expression.contains("@@")) {
			DriverManagerDataSource dds = new DriverManagerDataSource();
			List<String> allGroups = ReUtil.findAll(compile("@@(.*?)\\((.*?)\\)"), expression, 0);
			for (String group : allGroups) {
				String dataView = StrUtil.subBetween(group, "@@", "(");
				GenDataView genDataView = buildDdsAndArgs(group, dds);
				if ("select".equals(genDataView.getType())) {
					List<Map<String, Object>> dbList = new JdbcTemplate(dds).queryForList(genDataView.getSql(),
							Stream.of(genDataView.getParams().split(StrUtil.COMMA)).filter(StrUtil::isNotBlank).map(
									i -> FormUtils.getVal(i, context)).toArray());
					context.put(dataView, dbList);
					s = ReUtil.replaceAll(expression, group, "#" + dataView);
				}

			}
		}
		return FormUtils.getVal(s, context);
	}

	public FormInfo buildFormInfo(GenTableInfo genTableInfo, GenFormDo genFormDo, OgnlContext context) {
		FormInfo formInfo = new FormInfo();
		formInfo.setTableId(genFormDo.getDoTableId());
		Map<String, String> params = new HashMap<>();
		genFormDo.getGenFormDoColumnList().forEach(i -> {
			if (StrUtil.isNotBlank(i.getVal())) {
				genTableInfo.getGenColumnInfoList().stream().filter(c -> c.getId().equals(i.getColumnId())).findAny().ifPresent(
						c -> params.put(c.getName(), getVal(i.getVal(), context).toString()));

			}
		});
		return formInfo.setParams(params);
	}

	private void saveAndPut(GenTableInfo genTableInfo, GenFormDo genFormDo, OgnlContext context) {
		FormInfo formInfo = buildFormInfo(genTableInfo, genFormDo, context);
		Integer newId = formInfoService.save(formInfo);
		Map<String, Object> newOne = formInfoService.getOne(new QueryFormInfo().setTableId(genFormDo.getDoTableId()).setCondition(new HashMap<String, String>() {{
			put("id", String.valueOf(newId));
		}}));
		context.put(genFormDo.getName(), newOne);
	}
}
